#include "Multitree.h"
#include "Exceptions.h"
#include <math.h>
#include <assert.h>
namespace SymCe{

	Number numeric_function(FunctionType functtype, Number num);
	Number numeric_operator(Multitree * tree);

	//this function should get tree without any variables and return number as a result
	Number numeric(Multitree * tree){
		Number num;
		Number num2;
		if (tree == NULL){
			return Number(0);
		}
		switch(tree->symbol.type){
			case NUMBER:
			case CONSTANT:
				return tree->symbol.number;
				break;
			case FUNCTION:
				num = numeric(tree->childs->subtree);
				num2 = numeric_function(tree->symbol.func, num);
				return num2;
				break;
			case OPERATOR:
				return numeric_operator(tree);
				break;
			default:
				assert (tree->symbol.type == VARIABLE);
				//we cannot return real number here.
				//we should throw something
				throw UnknownVariable();
				return Number(0);
		}
		return Number(0);
	}

	Number numeric_operator(Multitree * tree){
		Number result;
		Number tmp1;
		Number tmp2;
		ListOfChilds * pass;
		assert(tree->symbol.type == OPERATOR);
		pass = tree->childs;
		if(tree->symbol.oper == POWER){
			tmp1 = numeric(tree->childs->subtree); //base
			tmp2 = numeric(tree->childs->next->subtree); //exponent
			//we have numbers, now let's just make power of them
			result = tmp1^tmp2;
		}
		else{
			pass = tree->childs;
			if(tree->symbol.oper == PLUS){
				result = 0;
				while (pass != NULL){
					tmp1 = numeric(pass->subtree);
					if (pass->inverted){
						result = result - tmp1;
					}else{
						result = result + tmp1;
					}
					pass = pass->next;
				}
			}else{
				result = 1;
				while (pass != NULL){
					tmp1 = numeric(pass->subtree);
					if (pass->inverted){
						if (tmp1 == 0){
							throw ZeroDivision();
						}
						result = result / tmp1;
					}else{
						result = result * tmp1;
					}
					pass = pass->next;
				}
			}
		}
		return result;
	}

	Number numeric_function(FunctionType functtype, Number num){
		switch(functtype){
			case COS:
				if(num == Number(0)){
					return Number(1);
				}else{
					return Number(cos(num.get_double()));
				}
				break;
			case SIN:
				if(num == Number(0)){
					return Number(0);
				}else{
					return Number(sin(num.get_double()));
				}
				break;
			case TAN:
				if(num == Number(0)){
					return Number(0);
				}else{
					return Number(tan(num.get_double()));
				}
				break;
			case LN:
				if(num == Number(1)){
					return Number(0);
				}else{
					return Number(log(num.get_double()));
				}
				break;
		}
		return Number (0);
	}
}
